//
// Created by snake on 2022/11/8.
//

#include <tuple>
#include <fstream>
#include <string>
#include <optional>
#include <stack>
#include <fmt/core.h>
#include <thread>
#include <mutex>
#include <vector>

#include "arg.hpp"
#include "file.hpp"


using namespace std;
namespace fs = filesystem;

/**
 * 计算单个文件行数信息
 * @param filename
 * @return 非空行数, 空行数, 总行数
 */
optional<tuple<int, int, int>> cnt_line(string &filename) {
    int nblank_cnt = 0, blank_cnt = 0;
    ifstream ifs;
    ifs.open(filename);
    if (!ifs.is_open()) {
        fmt::print("can't open {}", filename);
        return nullopt;
    }
    string buf;
    while (getline(ifs, buf)) {
        //  不同系统空行不同
#ifdef _WIN32
        buf.empty()
#else
        buf == "\r"
        #endif
        ? blank_cnt++ : nblank_cnt++;
        // fmt::print("n{} b{}\t{}\n", nblank_cnt, blank_cnt, buf);
    }

    // 最后空行不会计算
    blank_cnt += buf.empty();

    return make_tuple(nblank_cnt, blank_cnt, nblank_cnt + blank_cnt);
}

int main(int argc, char *argv[]) {
    auto args = parse_serv_args(argc, argv);

    fmt::print("all: {}, recursive: {}, ignore: {}, suffix: {} thread: {}\n",
               args.all, args.recursive, args.ignore_blank, args.suffix, args.thread);

    // 文件清单
    auto file_list = stack<string>();
    for (int i = optind; i < argc; ++i) {
        string filename = argv[i];
        switch (fs::status(filename).type()) {
            case fs::file_type::regular:
                if (filename.ends_with(args.suffix)) {
                    file_list.emplace(filename);
                }
                break;
            case fs::file_type::directory:
                if (args.recursive) {
                    getfilelist_r(filename, file_list, args.suffix);
                } else {
                    for (auto &p: fs::directory_iterator(filename)) {
                        auto pth = p.path().string();
                        if (fs::is_regular_file(p) && pth.ends_with(args.suffix)) {
                            file_list.emplace(pth);
                        }
                    }
                }
                break;
            default:
                break;
        }
    }



    // file list, stdout
    mutex mtx1, mtx2;
    vector<thread> pool;
    pool.reserve(args.thread);
    for (int i = 0; i < args.thread; ++i) {
        thread t([&] {
            while (true) {
                unique_lock grd(mtx1);
                if (file_list.empty()) break;
                string filename = file_list.top();
                file_list.pop();
                grd.unlock();

                auto [nblank_cnt, blank_cnt, total] = *cnt_line(filename);
                unique_lock grd2(mtx2);
                cout << (this_thread::get_id()) << ":" << filename << "\tn" << nblank_cnt << endl;
                // thread safe
                // fmt::print("{} :n{} b{} t{}\n", filename, nblank_cnt, blank_cnt, total);
            }
        });
        // 延长生命周期
        pool.emplace_back(std::move(t));
    }

    // 等待结束
    for (auto &item: pool) {
        item.join();
    }

    // 所有子线程已经结束
    fmt::print("end\n");
    return 0;
}
